/**
 * @author		Ben Layet
 * @version 	1.0
 * @date		24 Sep 2008
 */
package org.ignoramus.common.collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * Observable collection.
 */
public class ObservableCollection<E> extends DecoratedCollection<E>
{
	/** The list of listeners. */
	private final List<CollectionListener<E>> listeners = Collections.synchronizedList(new ArrayList<CollectionListener<E>>());
	
	/**
	 * @param collection
	 */
	public ObservableCollection(Collection<E> collection)
	{
		super(collection);
	}

	/**
	 * Adds a listener.
	 * @param l the listener to add.
	 */
	public void addCollectionListener(CollectionListener<E> l)
	{
		listeners.add(l);
	}

	/**
	 * Removes a listener.
	 * @param l the listener to remove.
	 */
	public void removeCollectionListener(CollectionListener<E> l)
	{
		listeners.remove(l);
	}


	/**
	 * Notify the listeners of a modification.
	 */
	private void notifyListeners()
	{
		//create the event
		CollectionEvent<E> evt = new CollectionEvent<E>(this);
		//loop through the listeners
		synchronized (listeners)
		{
			for (Iterator<CollectionListener<E>> iterator = listeners.iterator(); iterator.hasNext();)
			{
				iterator.next().collectionModified(evt);
			}
		}
	}

	/**
	 * Creates an iterator wrapping up the given iterator.
	 * @param iterator an iterator to decorate.
	 * @return The decorated iterator.
	 */
	protected DecoratedIterator<E> createIterator(Iterator<E> iterator)
	{
		return new ObservableCollectionIterator<E>(iterator);
	}
	// Modification Operations

	/**
	 * Ensures that this collection contains the specified element (optional
	 * operation). Returns <tt>true</tt> if this collection changed as a result
	 * of the call. (Returns <tt>false</tt> if this collection does not permit
	 * duplicates and already contains the specified element.)
	 * 
	 * @param e
	 *            element whose presence in this collection is to be ensured
	 * @return <tt>true</tt> if this collection changed as a result of the call
	 */
	public boolean add(E e)
	{
		//execute the modification
		boolean ret = super.add(e);
		//notify the listeners
		notifyListeners();
		//return the result
		return ret;
	}

	/**
	 * Removes a single instance of the specified element from this collection,
	 * if it is present (optional operation). More formally, removes an element
	 * <tt>e</tt> such that
	 * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>, if this
	 * collection contains one or more such elements. Returns <tt>true</tt> if
	 * this collection contained the specified element (or equivalently, if this
	 * collection changed as a result of the call).
	 * 
	 * @param o
	 *            element to be removed from this collection, if present
	 * @return <tt>true</tt> if an element was removed as a result of this call
	 */
	public boolean remove(Object o)
	{
		//execute the modification
		boolean ret = super.remove(o);
		//notify the listeners
		notifyListeners();
		//return the result
		return ret;
	}
	/**
	 * Adds all of the elements in the specified collection to this collection
	 * (optional operation). The behavior of this operation is undefined if the
	 * specified collection is modified while the operation is in progress.
	 * (This implies that the behavior of this call is undefined if the
	 * specified collection is this collection, and this collection is
	 * nonempty.)
	 * 
	 * @param c
	 *            collection containing elements to be added to this collection
	 * @return <tt>true</tt> if this collection changed as a result of the call
	 */
	public boolean addAll(Collection<? extends E> c)
	{
		//execute the modification
		boolean ret = super.addAll(c);
		//notify the listeners
		notifyListeners();
		//return the result
		return ret;
	}

	/**
	 * Removes all of this collection's elements that are also contained in the
	 * specified collection (optional operation). After this call returns, this
	 * collection will contain no elements in common with the specified
	 * super.
	 * 
	 * @param c
	 *            collection containing elements to be removed from this
	 *            collection
	 * @return <tt>true</tt> if this collection changed as a result of the call
	 */
	public boolean removeAll(Collection<?> c)
	{
		//execute the modification
		boolean ret = super.removeAll(c);
		//notify the listeners
		notifyListeners();
		//return the result
		return ret;
	}

	/**
	 * Removes all of the elements from this collection (optional operation).
	 * The collection will be empty after this method returns.
	 */
	public void clear()
	{
		//execute the modification
		super.clear();
		//notify the listeners
		notifyListeners();
	}
	/**
	 * Returns an iterator over the elements in this collection. There are no
	 * guarantees concerning the order in which the elements are returned
	 * (unless this collection is an instance of some class that provides a
	 * guarantee).
	 * 
	 * @return an <tt>Iterator</tt> over the elements in this collection
	 */
	public Iterator<E> iterator(Iterator<E> iterator)
	{
		return new ObservableCollectionIterator<E>(super.iterator());
	}
	
	/**
	 * Iterator wrapping up a normal iterator.
	 */
	@SuppressWarnings("hiding")
	public class ObservableCollectionIterator<E> extends DecoratedIterator<E>
	{
		/**
		 * Sole constructor.
		 * 
		 * @param iterator
		 *            The wrapped up iterator.
		 */
		public ObservableCollectionIterator(Iterator<E> iterator)
		{
			super(iterator);
		}

		/**
		 * 
		 * Removes from the underlying collection the last element returned by
		 * the iterator (optional operation). This method can be called only
		 * once per call to <tt>next</tt>. The behavior of an iterator is
		 * unspecified if the underlying collection is modified while the
		 * iteration is in progress in any way other than by calling this
		 * method.
		 * 
		 * @exception UnsupportedOperationException
		 *                if the <tt>remove</tt> operation is not supported by
		 *                this Iterator.
		 * 
		 * @exception IllegalStateException
		 *                if the <tt>next</tt> method has not yet been called,
		 *                or the <tt>remove</tt> method has already been called
		 *                after the last call to the <tt>next</tt> method.
		 */
		@Override
		public void remove()
		{
			super.remove();
			ObservableCollection.this.notifyListeners();
		}

	}

	
	
}
